import copy
import datetime
import json

import requests.utils

from aweirunner.utils.report import ConvertRequest
from httprunner import parser, HttpRunner
from bs4 import BeautifulSoup
from auto_dj_test.conf.MyLogger import MyLogger
from aweirunner import models
from httprunner.exceptions import FunctionNotFound, VariableNotFound
from requests.cookies import RequestsCookieJar
logger = MyLogger(__name__).logger

TEST_NOT_EXISTS = {"code": "0102", "status": False, "msg": "节点下没有接口或者用例集"}


def parse_tests(testcases, debugtalk, name=None, config=None, project=None):
    """转换接口测试用例的结构
    testcases: list
    config: none or dict
    debugtalk: dict
    """
    refs = {"env": {}, "def-api": {}, "def-testcase": {}, "debugtalk": debugtalk}

    testset = {
        "config": {"name": testcases[-1]["name"], "variables": []},
        "teststeps": testcases,
    }

    if config:
        testset["config"] = config

    if name:
        testset["config"]["name"] = name

    # 获取当前项目的全局变量
    logger.info("开始获取当前项目的全局变量")
    global_variables = (
        # 只获取数据库中的 key 和 value 字段的数据
        models.Variables.objects.filter(project=project).all().values("key", "value")
    )
    logger.info(f"获取当前项目的全局变量{global_variables}")
    # 获取所有的key
    all_config_variables_keys = set().union(
        *(d.keys() for d in testset["config"].setdefault("variables", []))
    )
    logger.info(f"获取所有的key{all_config_variables_keys}")
    # 全局变量
    global_variables_list_of_dict = []
    for item in global_variables:
        if item["key"] not in all_config_variables_keys:
            global_variables_list_of_dict.append({item["key"]: item["value"]})
    logger.info(f"全局变量{global_variables_list_of_dict}")

    # 有variables就直接extend,就设置key:variables value:[] 再extend
    # 配置的variables和全局变量重叠,优先使用配置中的variables
    testset["config"].setdefault("variables", []).extend(global_variables_list_of_dict)
    testset["config"]["refs"] = refs

    # 配置中的变量和全局变量合并
    variables_mapping = {}
    if config:
        for variables in config["variables"]:
            variables_mapping.update(variables)

    # 替换extract,validate中的变量和函数，只对value有效，key无效
    for testcase in testcases:
        extract: list = testcase.get("extract", [])
        validate: list = testcase.get("validate", [])
        api_variables: list = testcase.get("variables", [])
        parse_validate_and_extract(
            extract, variables_mapping, {}, api_variables
        )
        parse_validate_and_extract(
            validate, variables_mapping, {}, api_variables
        )

    return testset


def parse_validate_and_extract(list_of_dict: list, variables_mapping: dict, functions_mapping, api_variables: list):
    """
    Args:
        list_of_dict (list)
        variables_mapping (dict): variables mapping.
        functions_mapping (dict): functions mapping.
        api_variables: (list)
    Returns:
        引用传参，直接修改dict的内容，不需要返回
    """

    # 获取api中所有变量的key
    api_variables_key = []
    for variable in api_variables:
        api_variables_key.extend(list(variable.keys()))

    for index, d in enumerate(list_of_dict):
        is_need_parse = True
        # extract: d是{'k':'v'}, v类型是str
        # validate: d是{'equals': ['v1', 'v2']}， v类型是list
        v = list(d.values())[0]
        try:

            # validate,extract 的值包含了api variable的key中，不需要替换
            for key in api_variables_key:
                if isinstance(v, str):
                    if key in v:
                        is_need_parse = False
                elif isinstance(v, list):
                    # v[1]需要统一转换成str类型，否则v[1]是int类型就会报错
                    if key in str(v[1]):
                        is_need_parse = False

            if is_need_parse is True:
                d = parser.parse_data(
                    d,
                    variables_mapping=variables_mapping,
                    functions_mapping=functions_mapping,
                )
                for k, v in d.items():
                    v = parser.parse_string_functions(
                        v,
                        variables_mapping=variables_mapping,
                        functions_mapping=functions_mapping,
                    )
                    d[k] = v
                list_of_dict[index] = d
        except (FunctionNotFound, VariableNotFound):
            continue


def debug_api(api, project, name=None, config=None, save=True, user=""):
    """
    :param api: dict or list
    :param project: int
    :param name:  str
    :param config: str
    :param save:
    :param user:
    :return:
    """
    if len(api) == 0:
        return TEST_NOT_EXISTS

    # testcase 多个接口时候
    if isinstance(api, dict):
        """
        [{}]
        """
        logger.debug("多个接口")
        api = [api]

    if config and config.get("parameters"):
        logger.debug("参数化过滤，只加载api中调用到的参数")
        api_params = []
        for item in api:
            # 如果该值中没有 "params" 键，则尝试获取 "json" 键对应的值。
            params = item['request'].get("params") or item["request"].get("json")
            logger.info(f"过滤前params{params}")
            for v in params.values():
                if type(v) == list:
                    api_params.extend(v)
                else:
                    api_params.append(v)
        parameters = []
        for index, dic in enumerate(config['parameters']):
            for key in dic.keys():
                # key可能是key-key1这种模式,所以需要分割
                for i in key.split("-"):
                    if "$" + i in api_params:
                        parameters.append(dic)
            config["parameters"] = parameters
        logger.info(f"过滤后parameters [] --->{parameters}")

    try:
        logger.debug("第三阶段开始解析")
        testcase_list = [
            parse_tests(
                api, {"variables": {}, "functions": {}}, name=name, config=config, project=project
            )
        ]
        logger.info(f"第三阶段解析完成{testcase_list}")
        kwargs = {"failfast": False}
        # 运行 接口
        runner = HttpRunner(**kwargs)
        runner.run(testcase_list)

        # 将结果进行格式化
        summary = parse_summary(runner.summary)
        logger.info(f"将结果进行格式化之后summary  {summary}")

        # 保存在报告里面
        if save:
            save_summary(name, summary, project, type=1, user=user)

        # 复制一份response的json
        for details in summary.get("details", []):
            for record in details.get("records", []):
                json_data = record["meta_data"]["response"].pop("json", {})
                if json_data:
                    record["meta_data"]["response"]["jsonCopy"] = json_data
        # ConvertRequest.generate_curl(
        #     summary["details"], convert_type=("curl", "boomer")
        # )
        return summary
    except Exception as e:
        logger.error(f"debug_api error: {e}")
        raise SyntaxError(str(e))


def parse_summary(summary):
    """序列化summary"""
    for detail in summary["details"]:

        for record in detail["records"]:

            for key, value in record["meta_data"]["request"].items():
                if isinstance(value, bytes):
                    record["meta_data"]["request"][key] = value.decode("utf-8")
                if isinstance(value, RequestsCookieJar):
                    record["meta_data"]["request"][
                        key
                    ] = requests.utils.dict_from_cookiejar(value)

            for key, value in record["meta_data"]["response"].items():
                if isinstance(value, bytes):
                    record["meta_data"]["response"][key] = value.decode("utf-8")
                if isinstance(value, RequestsCookieJar):
                    record["meta_data"]["response"][
                        key
                    ] = requests.utils.dict_from_cookiejar(value)

            if "text/html" in record["meta_data"]["response"]["content_type"]:
                record["meta_data"]["response"]["content"] = BeautifulSoup(
                    record["meta_data"]["response"]["content"], features="html.parser"
                ).prettify()

    return summary


def save_summary(name, summary, project, type=2, user=""):
    """保存报告信息"""
    if "status" in summary.keys():
        return
    if name == "" or name is None:
        name = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # 需要先复制一份,不然会把影响到debug_api返回给前端的报告
    summary = copy.copy(summary)
    summary_detail = summary.pop("details")
    report = models.Report.objects.create(
        **{
            "project": models.Project.objects.get(id=project),
            "name": name,
            "type": type,
            "status": summary["success"],
            "summary": json.dumps(summary, ensure_ascii=False),
            "creator": user,
        }
    )

    models.ReportDetail.objects.create(summary_detail=summary_detail, report=report)
    return report.id